package ln.gather.jsr.jndi.repository;

import ln.gather.jsr.jndi.function.ThrowableFunction;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;

/**
 * 组件仓库抽象
 */
public abstract class AbstractComponentRepository implements ComponentRepository {

    protected final Logger logger = Logger.getLogger(this.getClass().getName());

    private Map<String, Object> componentCache = new ConcurrentHashMap<>();


    /**
     * 根据名称获取实例
     *
     * @param name 实例名称
     */
    @Override
    public <C> C getComponent(String name) {
        return (C) componentCache.computeIfAbsent(name, this::loadComponent);
    }

    /**
     * 获取仓库中所有的组件名称
     */
    @Override
    public Set<String> getComponentNames() {
        return componentCache.isEmpty() ? listComponentNames() : componentCache.keySet();
    }

    /**
     * 将仓库中的组件以Map的形式输出
     */
    @Override
    public Map<String, Object> getComponents() {
        return componentCache;
    }

    /**
     * 销毁
     */
    @Override
    public void destroy() {
        // 清除仓库缓存数据
        componentCache.clear();
        // 执行销毁，交给子类实现
        doDestroy();
    }

    /**
     * 销毁
     */
    protected abstract void doDestroy();

    /**
     * 加载组件，需要子类实现
     */
    public abstract <C> C loadComponent(String name);

    /**
     * 遍历所有组件的名称，需要子类实现
     */
    public abstract Set<String> listComponentNames();


    /**
     * 计算结果，忽略异常
     */
    protected <T, R> R executeInContextIgnoreException(T arg, ThrowableFunction<T, R> function) {
        return executeInContext(arg, function, false);
    }

    /**
     * 指定是否忽略异常来计算结果
     *
     * @param arg             方法函数的参数
     * @param function        方法函数
     * @param ignoreException 是否忽略异常
     */
    protected <T, R> R executeInContext(T arg, ThrowableFunction<T, R> function, boolean ignoreException) {
        R re = null;
        try {
            re = ThrowableFunction.execute(arg, function);
        } catch (RuntimeException e) {
            if (ignoreException) {
                logger.warning(e.getMessage());
            } else {
                throw new RuntimeException(e);
            }
        }
        return re;
    }
}
